home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CU Amiga Super CD-ROM 18
/
CU Amiga Magazine's Super CD-ROM 18 (1997)(EMAP Images)(GB)[!][issue 1998-01].iso
/
CUCD
/
Programming
/
ModemLink
/
Source
/
ModemLinkTask.c
< prev
next >
Wrap
C/C++ Source or Header
|
1997-10-24
|
14KB
|
484 lines
/*
** NAME: ModemLinkTask.c
** DESC: This routine contains all the code for the built-in Stop & Wait
** protocol. This is spawned by modemlink from the Link.c module.
** Other routines in this file are support routines for the protocol.
**
** AUTHOR: DATE: DESCRIPTION:
** ~~~~~~~~~~~~~~ ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
** Mike Veroukis 06 Apr 1997 Created
*/
#include <exec/errors.h>
#include <exec/io.h>
#include <exec/memory.h>
#include <exec/ports.h>
#include <exec/types.h>
#include <devices/serial.h>
#include <devices/timer.h>
#include <dos/dosextens.h>
#include <dos/dostags.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <string.h>
#include <stdio.h>
#include <dos.h>
#include "ModemLinkTask.h"
#include "Link.h"
#include "ModemLinkAPI.h"
#include "CRC.h"
#include "DeviceStuff.h"
char ReadPacketNum(struct IOExtSer *SerReadIO)
{
char PktLong = 0;
if (SerReadIO) {
SerReadIO->IOSer.io_Command = CMD_READ;
SerReadIO->IOSer.io_Length = sizeof(char);
SerReadIO->IOSer.io_Data = (APTR)&PktLong;
// To be changed to TimedIO() later on
DoIO((struct IORequest *) SerReadIO);
}
return (PktLong);
}
int ReadPacketWord(struct IOExtSer *SerReadIO)
{
int PktWord = -1;
if (SerReadIO) {
SerReadIO->IOSer.io_Command = CMD_READ;
SerReadIO->IOSer.io_Length = sizeof(int);
SerReadIO->IOSer.io_Data = (APTR)&PktWord;
// To be changed to TimedIO() later on
DoIO((struct IORequest *) SerReadIO);
}
return (PktWord);
}
LONG ReadPacketLong(struct IOExtSer *SerReadIO)
{
LONG PktLong = -1;
if (SerReadIO) {
SerReadIO->IOSer.io_Command = CMD_READ;
SerReadIO->IOSer.io_Length = sizeof(LONG);
SerReadIO->IOSer.io_Data = (APTR)&PktLong;
// To be changed to TimedIO() later on
DoIO((struct IORequest *) SerReadIO);
}
return (PktLong);
}
BYTE *ReadPacketData(struct IOExtSer *SerReadIO, LONG Length)
{
BYTE *PktData = NULL;
if (SerReadIO && Length > 0) {
if (PktData = (char *)AllocMem(Length, MEMF_PUBLIC)) {
SerReadIO->IOSer.io_Command = CMD_READ;
SerReadIO->IOSer.io_Length = Length;
SerReadIO->IOSer.io_Data = (APTR)PktData;
// To be changed to TimedIO() later on
DoIO((struct IORequest *) SerReadIO);
}
}
return (PktData);
}
int ReadPacket(struct IOExtSer *SerIO, struct LinkPkt **Pkt, UBYTE CurPktNum)
{
UBYTE PktNum;
ULONG CRC;
int err = 0;
if (*Pkt = ML_AllocPkt()) {
PktNum = ReadPacketNum(SerIO);
SerIO->IOSer.io_Command = CMD_READ;
SerIO->IOSer.io_Length = (ULONG)&((*Pkt)->Data) - (ULONG)&((*Pkt)->Length);
SerIO->IOSer.io_Data = &((*Pkt)->Length);
DoIO((struct IORequest *) SerIO);
(*Pkt)->Data = ReadPacketData(SerIO, (*Pkt)->Length);
if (PktNum != CurPktNum)
err = RPERR_PKTNUM;
CRC = GetCRC((*Pkt)->Data, (*Pkt)->Length);
if (CRC != (*Pkt)->CRC)
err = RPERR_PKTCRC;
if (err) {
ML_FreePkt(*Pkt);
}
}
else
err = RPERR_NOPKT;
return (err);
}
void WritePacket(struct IOExtSer *SerIO, struct LinkPkt *Pkt, UBYTE CurPktNum)
{
UBYTE buf[2];
if (SerIO && Pkt) {
buf[0] = SOH;
buf[1] = CurPktNum;
Pkt->CRC = GetCRC(Pkt->Data, Pkt->Length);
SerIO->IOSer.io_Command = CMD_WRITE;
SerIO->IOSer.io_Length = 2 * sizeof(char);
SerIO->IOSer.io_Data = (APTR)buf;
DoIO((struct IORequest *) SerIO);
SerIO->IOSer.io_Command = CMD_WRITE;
SerIO->IOSer.io_Length = (ULONG)&(Pkt->Data) - (ULONG)&(Pkt->Length);
SerIO->IOSer.io_Data = &(Pkt->Length);
DoIO((struct IORequest *) SerIO);
SerIO->IOSer.io_Command = CMD_WRITE;
SerIO->IOSer.io_Length = Pkt->Length;
SerIO->IOSer.io_Data = (APTR)Pkt->Data;
DoIO((struct IORequest *) SerIO);
}
}
void SendCode(struct IOExtSer *SerWriteIO, BYTE Code, BYTE CurPktNum)
{
UBYTE buf[2];
if (SerWriteIO) {
buf[0] = Code;
buf[1] = CurPktNum;
SerWriteIO->IOSer.io_Command = CMD_WRITE;
SerWriteIO->IOSer.io_Data = (APTR)buf;
if (CurPktNum < 0)
SerWriteIO->IOSer.io_Length = 1;
else
SerWriteIO->IOSer.io_Length = 2;
// To be changed to TimedIO() later on
DoIO((struct IORequest *) SerWriteIO);
}
}
void ResetTimer(struct timerequest *TimerIO)
{
DoAbortIO((struct IORequest *) TimerIO);
TimerIO->tr_node.io_Command = TR_ADDREQUEST;
TimerIO->tr_time.tv_secs = 3;
TimerIO->tr_time.tv_micro = 0;
SendIO((struct IORequest *) TimerIO);
}
void SetTimer(struct timerequest *TimerIO)
{
TimerIO->tr_node.io_Command = TR_ADDREQUEST;
TimerIO->tr_time.tv_secs = 3;
TimerIO->tr_time.tv_micro = 0;
SendIO((struct IORequest *) TimerIO);
}
void __saveds __asm AckTask(register __d0 ULONG Len, register __a0 char *PortName)
{
struct IOExtSer *SerIO; // points to the SerIO req. that's passed to us
struct IOExtSer *SerWriteIO; // is used exclusively for writing to serial device
struct IOExtSer *SerReadIO; // is used exclusively for reading from serial device
struct timerequest *TimerIO = NULL; // used for TIMEOUTs when waiting for ACK
struct MsgPort *SerWriteMP; // message port for SerWriteIO
struct MsgPort *SerReadMP; // message port for SerReadIO
struct MsgPort *TimerMP = NULL; // message port for TimerIO
struct MsgPort *LinkMP; // we get all our IO reqs through this port
struct LinkPkt OutPkt; // contents of packet to write to serial device
struct LinkPkt *InPkt = NULL; // points to packet just read from serial device
struct List InPktList; // list of packets read, but not yet sent to user
struct IOExtLink *ReadReq = NULL; // current user read IO request structure
struct IOExtLink *WriteReq = NULL; // current user write IO request structure
struct IOExtLink *Req; // next read/write IO request structure
ULONG SerWriteBit, SerReadBit; // signal bits for serial read/write
ULONG TimerBit, PktBit; // signal bits for timer and new LinkIO packet
ULONG WaitMask, Sig = 0; // Mask for Wait()
int err;
UBYTE CurInPktNum = 0;
UBYTE CurOutPktNum = 0;
UBYTE WaitACK = 0;
UBYTE WaitPacket = 0;
UBYTE ReadyToSend = 1;
UBYTE PktType;
NewList(&InPktList);
if (LinkMP = CreateMsgPort()) {
LinkMP->mp_Node.ln_Name = PortName;
LinkMP->mp_Node.ln_Pri = 0;
AddPort(LinkMP);
WaitPort(LinkMP);
Req = (struct IOExtLink *)GetMsg(LinkMP);
SerIO = (struct IOExtSer *)Req->IOLink.io_Data;
if (Req->IOLink.io_Device) {
putreg(REG_A6, (long)Req->IOLink.io_Device);
geta4();
}
ReplyMsg((struct Message *)Req);
if (OpenTimerDevice(&TimerMP, &TimerIO)) {
Req = NULL;
if (CloneIO((struct IORequest *)SerIO, &SerReadMP, (struct IORequest **)&SerReadIO)) {
if (CloneIO((struct IORequest *)SerIO, &SerWriteMP, (struct IORequest **)&SerWriteIO)) {
SerWriteBit = 1 << SerWriteMP->mp_SigBit;
SerReadBit = 1 << SerReadMP->mp_SigBit;
TimerBit = 1 << TimerMP->mp_SigBit;
PktBit = 1 << LinkMP->mp_SigBit;
WaitMask = SerWriteBit | SerReadBit | TimerBit | PktBit | SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D | SIGBREAKF_CTRL_E;
SerReadIO->IOSer.io_Command = CMD_READ;
SerReadIO->IOSer.io_Length = ID_LENGTH;
SerReadIO->IOSer.io_Data = (APTR)&PktType;
SendIO((struct IORequest *) SerReadIO);
while (!(Sig & SIGBREAKF_CTRL_C)) {
Sig = Wait(WaitMask);
if (WaitACK && CheckIO((struct IORequest *) TimerIO)) {
WaitIO((struct IORequest *) TimerIO);
WritePacket(SerWriteIO, &OutPkt, CurOutPktNum);
WaitACK = 1;
ResetTimer(TimerIO);
}
if (CheckIO((struct IORequest *) SerReadIO)) {
WaitIO((struct IORequest *) SerReadIO);
// remember, PktType is set by io_Data above
switch (PktType) {
case SOH:
err = ReadPacket(SerReadIO, &InPkt, CurInPktNum);
if (!err) {
AddTail(&InPktList, (struct Node *)InPkt);
SendCode(SerWriteIO, ACK, CurInPktNum);
CurInPktNum ^= 1;
}
else if (err == RPERR_PKTNUM)
SendCode(SerWriteIO, ACK, CurInPktNum ^ 1);
else
SendCode(SerWriteIO, GACK, -1);
if (WaitACK)
ResetTimer(TimerIO);
break;
case ACK:
if (CurOutPktNum == ReadPacketNum(SerReadIO)) {
DoAbortIO((struct IORequest *) TimerIO);
CurOutPktNum ^= 1;
ReadyToSend = 1;
ML_ReplyMsg(WriteReq);
WriteReq = NULL;
WaitACK = 0;
}
break;
case GACK:
WritePacket(SerWriteIO, &OutPkt, CurOutPktNum);
WaitACK = 1;
ResetTimer(TimerIO);
break;
case ENQ:
SendCode(SerWriteIO, ACK, -1);
break;
case CAN:
Signal(FindTask(NULL), SIGBREAKF_CTRL_C);
break;
}
SerReadIO->IOSer.io_Command = CMD_READ;
SerReadIO->IOSer.io_Length = ID_LENGTH;
SerReadIO->IOSer.io_Data = (APTR)&PktType;
SendIO((struct IORequest *) SerReadIO);
}
/*
** Check for aborted requests...
*/
if (Req && Req->IOLink.io_Error == IOERR_ABORTED) {
ML_ReplyMsg(Req);
Req = NULL;
}
if (ReadReq && ReadReq->IOLink.io_Error == IOERR_ABORTED) {
ML_ReplyMsg(ReadReq);
ReadReq = NULL;
WaitPacket = 0;
}
/*
** Read incomming packets
*/
if (!Req) {
Req = ML_GetMsg(LinkMP, ML_PIPE1);
}
if (Req && !ReadReq &&
(Req->IOLink.io_Command == MLCMD_READ ||
Req->IOLink.io_Command == CMD_READ)) {
ReadReq = Req;
Req = ML_GetMsg(LinkMP, ML_PIPE1);
WaitPacket = 1;
}
if (!WriteReq &&
(Req && Req->IOLink.io_Command == MLCMD_WRITE ||
Req && Req->IOLink.io_Command == CMD_WRITE)) {
WriteReq = Req;
Req = NULL;
if (Req && Req->IOLink.io_Command == MLCMD_WRITE)
CopyMem(WriteReq->IOLink.io_Data, &OutPkt, sizeof(struct LinkPkt));
else {
if (WriteReq->IOLink.io_Length < 0)
OutPkt.Length = strlen(WriteReq->IOLink.io_Data);
else
OutPkt.Length = WriteReq->IOLink.io_Length;
OutPkt.Data = WriteReq->IOLink.io_Data;
}
}
/*
** If ready to send Pkt and out going packet exists, send it.
*/
if (ReadyToSend && WriteReq) {
WriteReq->Flags = WriteReq->Flags | ML_PIPE2;
WritePacket(SerWriteIO, &OutPkt, CurOutPktNum);
if (WaitACK)
ResetTimer(TimerIO);
else
SetTimer(TimerIO);
WaitACK = 1;
ReadyToSend = 0;
}
/*
** If incomming Pkt is queued and we have Read req, reply it.
*/
if (ReadReq && WaitPacket && !(IsListEmpty(&InPktList))) {
InPkt = (struct LinkPkt *)RemHead(&InPktList);
if (ReadReq->IOLink.io_Command == CMD_READ) {
ReadReq->IOLink.io_Data = InPkt->Data;
ReadReq->IOLink.io_Length = InPkt->Length;
FreeMem(InPkt, sizeof (struct LinkPkt));
}
else
ReadReq->IOLink.io_Data = InPkt;
ML_ReplyMsg(ReadReq);
InPkt = NULL;
ReadReq = NULL;
WaitPacket = 0;
}
/*
** Check CTRL-C -- shut down if CTRL-C detected.
*/
if (Sig & SIGBREAKF_CTRL_C) {
if (WaitACK)
DoAbortIO((struct IORequest *) TimerIO);
SendCode(SerWriteIO, CAN, EOT);
if (WriteReq) {
WriteReq->IOLink.io_Error = IOERR_ABORTED;
ML_ReplyMsg(WriteReq);
}
if (ReadReq) {
ReadReq->IOLink.io_Error = IOERR_ABORTED;
ML_ReplyMsg(ReadReq);
}
}
/*
** ^C or ^E arrives
** this continues the shut down process from above and since the
** CLEAR command issued by ^E does the same thing, they share
** the same code:
** deallocate all buffered incomming packets
*/
if ((Sig & SIGBREAKF_CTRL_C) || (Sig & SIGBREAKF_CTRL_E)) {
while (InPkt = (struct LinkPkt *)RemHead(&InPktList)) {
FreeMem(InPkt->Data, InPkt->Length);
FreeMem(InPkt, sizeof(struct LinkPkt));
}
}
SerWriteIO->IOSer.io_Command = SDCMD_QUERY;
DoIO((struct IORequest *) SerWriteIO);
if (SerWriteIO->io_Status & (1 << 5))
Signal(FindTask(NULL), SIGBREAKF_CTRL_C);
}
DeleteIO_MP(SerWriteMP, (struct IORequest *) SerWriteIO);
}
DoAbortIO((struct IORequest *) SerReadIO);
DeleteIO_MP(SerReadMP, (struct IORequest *) SerReadIO);
}
if (WaitACK)
DoAbortIO((struct IORequest *)TimerIO);
SafeCloseDevice(TimerMP, (struct IORequest *)TimerIO);
}
/*
** remove all queued IO requests and remove MsgPort as well
*/
Forbid();
if (!Req)
Req = ML_GetMsg(LinkMP, ML_PIPE1);
while (Req) {
Req->IOLink.io_Error = IOERR_ABORTED;
ML_ReplyMsg(Req);
Req = ML_GetMsg(LinkMP, ML_PIPE1);
}
RemPort(LinkMP);
DeleteMsgPort(LinkMP);
Permit();
}
}